home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 050 / madtrb34.arc / LOAN.PAS < prev    next >
Pascal/Delphi Source File  |  1986-02-09  |  32KB  |  872 lines

  1. program Loan_Amortization;
  2.  
  3. { Copyright 1984, Steve Wood, precision logic systems. Placed
  4.   in the public domain for non-commercial use 2/1/86.
  5.   This program was thrown together to provide an example of
  6.   how to use T-SCREENs in an application. It has not been
  7.   thouroughly tested and probably has some bugs. Feel free
  8.   to use and modify it as you see fit. But, be aware that the
  9.   user is resposible for verifying the accuracy of the results.
  10.  
  11.   NOTE: To compile the loan demo for a system using a MONOCRHOME
  12.         monitor change VID_SEG and FILE_EXT in the typed constant
  13.         definitions as noted below. }
  14.  
  15. const  BS    = #8;      CUR_UP     = #72;      F1   = #59;
  16.        CR    = #13;     CUR_LEFT   = #75;      F9   = #67;
  17.        ESC   = #27;     CUR_RIGHT  = #77;      F10  = #68;
  18.        BL    = #32;     CHAR_INS   = #82;
  19.        OK    = 0;       CHAR_DEL   = #83;
  20.        UP    = -1;      VID_SEG    = $B800;   { Change to $B000 for mono.  }
  21.        DOWN  = 1;       VID_OFFSET = $0000;
  22.        TOF   = #12;     FILE_EXT   = '.TSC';  { Change to '.TSM' for mono. }
  23.                         ENTER      = #17#196#217;
  24.                         ARROW      = #205#205#16#32;
  25.  
  26. type   Fld_Parms   = record
  27.                        xloc       : Byte;
  28.                        yloc       : Byte;
  29.                        fld_len    : Byte;
  30.                        fld_type   : Char;
  31.                        fld_char   : Char;
  32.                        inp_attr   : Byte;
  33.                        disp_attr  : Byte;
  34.                        msg_ptr    : Byte;
  35.                      end;
  36.  
  37.        Input_Rec   = record
  38.                        borrower   : String[40];
  39.                        collateral : String[40];
  40.                        principle  : Real;
  41.                        rate       : Real;
  42.                        payment    : Real;
  43.                        pmts_per_yr: Integer;
  44.                        first_mo   : Integer;
  45.                        first_yr   : Integer;
  46.                        no_of_pmts : Integer;
  47.                        select_yr  : Integer;
  48.                        out_to     : Char;
  49.                      end;
  50.  
  51.        Inp_Scrn    = array[1..1792] of Integer;
  52.        Inp_Parm    = array[1..72] of Fld_Parms;
  53.        Inp_Buf     = record
  54.                        buf_scrn   : array[1..1760] of Integer;
  55.                        buf_parm   : Inp_Parm;
  56.                      end;
  57.        Inp_Lines   = array[1..25,1..80] of Integer;
  58.  
  59. var    fld_dat     : Inp_Parm;
  60.        inp_str     : String[80];
  61.        default     : String[80];
  62.        err_msg     : String[72];
  63.        retry       : String[30];
  64.        inp_rec     : Input_Rec;
  65.        inp_file    : File of Input_Rec;
  66.        vid_scrn    : Inp_Scrn  absolute  VID_SEG:VID_OFFSET;
  67.        vid_line    : Inp_Lines absolute  vid_scrn;
  68.        scrn_buf    : Inp_Buf;
  69.        prompt_buf  : Inp_Scrn;
  70.        prompt_line : Inp_Lines absolute prompt_buf;
  71.        temp_buf    : Inp_Scrn;
  72.        help_buf    : Inp_Scrn;
  73.        scrn_file   : File;
  74.        calc_rate,
  75.        pmt_cnt     : Real;
  76.        io_status,
  77.        last_yr,
  78.        direction   : Integer;
  79.        inchr       : Char;
  80.        incnt, j,
  81.        last_fld,
  82.        fld_no      : Byte;
  83.        data_ok,
  84.        msg_on,
  85.        esc_exit,
  86.        dos_exit    : Boolean;
  87.  
  88. function Fgnd(attr: Byte): Integer;
  89.     begin
  90.       Fgnd := (attr and $0F) + ((attr and $80) div 8);
  91.     end { Fgnd };
  92.  
  93. function Bgnd(attr: Byte): Integer;
  94.     begin
  95.       Bgnd := (attr and $70) div $10;
  96.     end { Bgnd };
  97.  
  98. procedure Beep;
  99.     begin
  100.       Sound(440); Delay(150); NoSound;
  101.     end { Beep };
  102.  
  103. procedure Clr_Kbd_Buf;
  104.     var kbd_buf  : Byte absolute $0000:$041A;
  105.     var kbd_clr  : Byte absolute $0000:$041C;
  106.     begin
  107.       kbd_buf := kbd_clr;
  108.     end { Clr_Kbd_Buf };
  109.  
  110. procedure BW_Vid;
  111.     begin
  112.       TextColor(Black); TextBackground(White);
  113.     end { BW_Vid };
  114.  
  115. procedure Rep_Str(chr: Char; len: Integer);
  116.     var  i  : Integer;
  117.     begin
  118.       for i := 1 to len do
  119.          Write(chr);
  120.     end { Rep_Str };
  121.  
  122. procedure Do_BackSpace(chr: Char);
  123.     begin
  124.       if incnt > 0 then
  125.         begin
  126.           inp_str[incnt] := BL;
  127.           incnt := incnt -1; Write(BS,chr,BS);
  128.         end
  129.       else Beep;
  130.     end { Do_BackSpace };
  131.  
  132. procedure Strip_Trailing_Blanks(len: Byte);
  133.    begin
  134.      While inp_str[len] = BL do
  135.        len := len - 1;
  136.      inp_str[0] := chr(len);
  137.    end;
  138.  
  139. procedure Strip_Leading_Blanks(len: Byte);
  140.    var  p : Byte;
  141.    begin
  142.      p := 1; default := inp_str;
  143.      While inp_str[p] = BL do p := p + 1;
  144.      if p > 1 then
  145.        begin default := Copy(inp_str,p,len); inp_str := default; end;
  146.    end;
  147.  
  148. procedure Strip_Blanks(len: Byte);
  149.     begin
  150.       Strip_Trailing_Blanks(len);
  151.       Strip_Leading_Blanks(len);
  152.     end;
  153.  
  154. procedure Disp_Msg;
  155.     var  msg_line  : Byte;
  156.     begin
  157.       if msg_on then
  158.         begin
  159.           msg_line := fld_dat[fld_no].msg_ptr + 9;
  160.           Move(prompt_line[msg_line],vid_line[24],160)
  161.         end
  162.       else
  163.     end { Disp_Msg };
  164.  
  165. procedure Clear_Prompt;
  166.     begin
  167.       Move(prompt_line[5],vid_line[23],160);
  168.       Move(prompt_line[5],vid_line[24],160);
  169.       Move(prompt_line[5],vid_line[25],160);
  170.     end { Clear_Prompt };
  171.  
  172. procedure Print_Prompt;
  173.     begin
  174.       Clear_Prompt; Move(prompt_line[21],vid_line[25],160);
  175.     end { Print_Prompt };
  176.  
  177. procedure Help_Prompt;
  178.     begin
  179.       Clear_Prompt; Move(prompt_line[22],vid_line[24],160);
  180.     end { Help_Prompt };
  181.  
  182. procedure Disp_Prompt(prmt_no: Byte);
  183.     begin
  184.       vid_line[23] := prompt_line[3 * prmt_no + 1];
  185.       vid_line[24] := prompt_line[3 * prmt_no + 2];
  186.       vid_line[25] := prompt_line[3 * prmt_no + 3];
  187.     end { Disp_Prompt };
  188.  
  189. procedure Disp_Help;
  190.     var xpos, ypos : Byte;
  191.     begin
  192.       Move(vid_scrn,temp_buf,3520);
  193.       Move(help_buf,vid_scrn,3520);
  194.       Help_Prompt;
  195.       xpos := WhereX; ypos := WhereY;
  196.       GoToXY(51,24); Read(Kbd,inchr); Clr_Kbd_Buf;
  197.       Move(temp_buf,vid_scrn,3520); GoToXY(xpos,ypos);
  198.     end;
  199.  
  200. procedure Do_Esc(seq: Byte; var end_fld: Boolean);
  201.     var  xchr      : Char;
  202.          temp_str  : String[80];
  203.     begin
  204.       if KeyPressed then with fld_dat[seq] do
  205.         begin
  206.           Read(Kbd,xchr);
  207.           case xchr of
  208.             CUR_UP     : if fld_no > 1 then
  209.                            begin direction := UP; end_fld := True; end
  210.                          else Beep;
  211.  
  212.             CUR_RIGHT  : if incnt < fld_len then
  213.                            begin
  214.                              incnt := incnt + 1;
  215.                              Strip_Trailing_Blanks(fld_len);
  216.                              if (incnt > length(inp_str)) and
  217.                                 (length(inp_str) < length(default)) then
  218.                                begin
  219.                                  inp_str[incnt] := default[incnt];
  220.                                  Write(inp_str[incnt]);
  221.                                end
  222.                              else GoToXY(WhereX + 1,WhereY);
  223.                            end
  224.                          else Beep;
  225.             CUR_LEFT   : if incnt > 0 then
  226.                            begin
  227.                              GoToXY(WhereX - 1, WhereY); incnt := incnt - 1;
  228.                            end
  229.                          else Beep;
  230.             CHAR_INS   : begin
  231.                            Strip_Trailing_Blanks(fld_len);
  232.                            Insert(BL,inp_str,incnt + 1);
  233.                            if length(inp_str) > fld_len then
  234.                              inp_str[0] := chr(fld_len);
  235.                            GoToXY(xloc,yloc); Write(inp_str);
  236.                            GoToXY(xloc + incnt,yloc);
  237.                          end;
  238.             CHAR_DEL   : begin
  239.                            Strip_Trailing_Blanks(fld_len);
  240.                            if (length(inp_str) > 0) and
  241.                               (incnt <= length(inp_str)) then
  242.                              begin
  243.                                Delete(inp_str,incnt + 1,1);
  244.                                GoToXY(xloc,yloc); Write(inp_str,fld_char);
  245.                                inp_str[length(inp_str) + 1] := BL;
  246.                                GoToXY(xloc + incnt,yloc);
  247.                              end;
  248.                          end;
  249.             F9         : begin
  250.                            msg_on := (not msg_on); Disp_Msg;
  251.                            if msg_on then
  252.                              Disp_Msg
  253.                            else
  254.                              Move(prompt_line[8],vid_line[24],160);
  255.                          end;
  256.             F10        : begin
  257.                            Disp_Help; Disp_Prompt(0); Disp_Msg; Clr_Kbd_Buf;
  258.                          end;
  259.             else         Beep;
  260.           end;
  261.           inchr := xchr;
  262.         end
  263.       else begin fld_no := last_fld + 1; esc_exit := True; end_fld := True; end;
  264.     end { Do_Esc };
  265.  
  266. procedure Do_Ctrl(fld_no: Byte; chr: Char; var end_fld: Boolean);
  267.     begin
  268.       case inchr of
  269.         CR         : begin direction := Down; end_fld := True; end;
  270.         BS         : Do_BackSpace(chr);
  271.         ESC        : Do_Esc(fld_no,end_fld);
  272.         else         Beep;
  273.       end;
  274.     end { Do_Ctrl };
  275.  
  276. procedure Init_Fld(col,row,len,attr: Byte; fill: Char);
  277.     var  i  : Byte;
  278.     begin
  279.       GoToXY(col,row); TextColor(Fgnd(attr)); TextBackground(Bgnd(attr));
  280.       for i := 1 to len do
  281.          begin Write(fill); inp_str[i] := BL; end;
  282.       GoToXY(col,row);
  283.     end { Init_Fld };
  284.  
  285. procedure Disp_If_Valid(len: Byte; num: Boolean);
  286.     var valid    : Boolean;
  287.     begin
  288.       if incnt < len then
  289.         begin
  290.           valid := (num and (inchr in ['0'..'9','.','-'])) or
  291.                    ((not num) and (inchr in [' '..'~']));
  292.           if valid then
  293.             begin
  294.               Write(inchr); incnt := incnt + 1; inp_str[incnt] := inchr;
  295.             end
  296.           else Beep;
  297.         end;
  298.     end { Disp_If_Valid };
  299.  
  300. procedure Re_Disp_Attr(seq: Byte);
  301.     begin
  302.       With fld_dat[seq] do
  303.         begin
  304.           TextColor(Fgnd(disp_attr)); TextBackground(Bgnd(disp_attr));
  305.           GoToXY(xloc,yloc); Rep_Str(BL,fld_len); GoToXY(xloc,yloc);
  306.         end;
  307.     end { Re_Disp_Attr };
  308.  
  309.  
  310. procedure Get_Field(seq: Byte);
  311.     var  end_fld, is_num, skip  : Boolean;
  312.          init_len               : Byte;
  313.     begin
  314.       With fld_dat[seq] do
  315.       begin
  316.         incnt := 0; if seq = 9 then init_len := 5 else init_len := fld_len;
  317.         Init_Fld(xloc,yloc,init_len,inp_attr,fld_char);
  318.         end_fld := False;
  319.         if fld_type in ['N','D'] then
  320.           is_num := True
  321.         else is_num := False;
  322.         skip := (fld_no = 9) and (inp_rec.payment > 0.0);
  323.         While ((not end_fld) and (not skip)) do
  324.           begin
  325.             Read(Kbd,inchr);
  326.             if inchr < ' ' then Do_Ctrl(seq,fld_char,end_fld)
  327.             else  Disp_If_Valid(fld_len,is_num);
  328.           end;
  329.         if incnt > 0 then Strip_Trailing_Blanks(fld_len);
  330.       end;
  331.     end { Get_Field };
  332.  
  333. procedure Define_Fld(seq,col,row,len,attr1,attr2: Byte; chr,typ: Char);
  334.     begin
  335.       With fld_dat[seq] do
  336.         begin
  337.           xloc := col; yloc := row; fld_len := len; inp_attr := attr1;
  338.           disp_attr := attr2; fld_char := chr; fld_type := typ;
  339.         end;
  340.     end { Define_Fld };
  341.  
  342. procedure Load_Screen;
  343.     begin
  344.       Assign(scrn_file,('LOAN'+FILE_EXT));
  345.       {$I-} Reset(scrn_file); {$I+} io_status := IOresult;
  346.       if io_status = OK then
  347.         begin
  348.           {$I-} BlockRead(scrn_file,scrn_buf,32); {$I+} io_status := IOresult;
  349.           if io_status = OK then
  350.             begin
  351.               Move(scrn_buf,vid_scrn,3520); Move(scrn_buf.buf_parm,fld_dat,576);
  352.             end;
  353.           Close(scrn_file);
  354.         end;
  355.     end { Load_Screen };
  356.  
  357. procedure Load_Prompts;
  358.     begin
  359.       Assign(scrn_file,('LOAN-PMT'+FILE_EXT));
  360.       {$I-} Reset(scrn_file); {$I+} io_status := IOresult;
  361.       if io_status = OK then
  362.         begin
  363.           {$I-} BlockRead(scrn_file,prompt_buf,28); {$I+} io_status := IOresult;
  364.           Close(scrn_file);
  365.         end;
  366.     end { Load Prompts };
  367.  
  368. procedure Load_Help;
  369.     begin
  370.       Assign(scrn_file,('LOAN-HLP'+FILE_EXT));
  371.       {$I-} Reset(scrn_file); {$I+} io_status := IOresult;
  372.       if io_status = OK then
  373.         begin
  374.           {$I-} BlockRead(scrn_file,help_buf,28); {$I+} io_status := IOresult;
  375.           Close(scrn_file);
  376.         end;
  377.     end { Load Help };
  378.  
  379. procedure Disp_Default;
  380.     var  real_val   : Real;
  381.          int_val    : Integer;
  382.     begin
  383.       With inp_rec do
  384.       case fld_no of
  385.         1     : begin
  386.                   inp_str := borrower; Write(inp_str); default := inp_str;
  387.                 end;
  388.         2     : begin
  389.                   inp_str := collateral; Write(inp_str); default := inp_str;
  390.                 end;
  391.         3     : begin
  392.                   Str(principle:11:2,inp_str); Strip_Blanks(length(inp_str));
  393.                   Val(inp_str,real_val,io_status); Write(real_val:11:2);
  394.                 end;
  395.         4     : begin
  396.                   Str(rate:5:3,inp_str); Strip_Blanks(length(inp_str));
  397.                   Val(inp_str,real_val,io_status);
  398.                   Write(real_val:5:3);
  399.                 end;
  400.         5     : begin
  401.                   Str(payment:11:2,inp_str); Strip_Blanks(length(inp_str));
  402.                   Val(inp_str,real_val,io_status);
  403.                   Write(real_val:11:2);
  404.                 end;
  405.         6     : begin
  406.                   Str(pmts_per_yr:2,inp_str); Strip_Blanks(length(inp_str));
  407.                   Val(inp_str,int_val,io_status);
  408.                   Write(int_val:2);
  409.                 end;
  410.         7     : begin
  411.                   Str(first_mo:2,inp_str); Strip_Blanks(length(inp_str));
  412.                   Val(inp_str,int_val,io_status);
  413.                   Write(int_val:2);
  414.                 end;
  415.         8     : begin
  416.                   Str(first_yr:2,inp_str); Strip_Blanks(length(inp_str));
  417.                   Val(inp_str,int_val,io_status);
  418.                   Write(int_val:2);
  419.                 end;
  420.         9     : begin
  421.                   Str(no_of_pmts:3,inp_str); Strip_Blanks(length(inp_str));
  422.                   Val(inp_str,int_val,io_status); real_val := int_val;
  423.                   Write(real_val:5:2);
  424.                 end;
  425.         10    : begin
  426.                   Str(select_yr:2,inp_str); Strip_Blanks(length(inp_str));
  427.                   Val(inp_str,int_val,io_status);
  428.                   Write(int_val:2);
  429.                 end;
  430.         11    : begin
  431.                   inp_str := out_to; Write(inp_str); default := inp_str;
  432.                 end;
  433.       end;
  434.     end { Disp_Default };
  435.  
  436. procedure Calc_No_Pmts;
  437.     begin
  438.       With inp_rec do
  439.       pmt_cnt := -(Ln(1 - (principle * calc_rate / payment))
  440.                     / Ln((1.0 + calc_rate)));
  441.     end { Calc_No_Pmts };
  442.  
  443. procedure Edit_Input(var input_ok: Boolean);
  444.     var  real_val : Real;
  445.          int_val  : Integer;
  446.  
  447.     function No_Of_Mos: Integer;
  448.         begin
  449.           No_Of_Mos := Trunc(12 / inp_rec.pmts_per_yr * pmt_cnt);
  450.         end { No_Of_Mos };
  451.  
  452.     procedure Calc_Last_Yr;
  453.         begin
  454.           With inp_rec do
  455.           begin
  456.           last_yr := Trunc((No_of_Mos + first_mo - 2) div 12 + first_yr);
  457.           end;
  458.         end;
  459.  
  460.     begin
  461.       input_ok := True; Re_Disp_Attr(fld_no);
  462.       err_msg := 'Please verify that the data entered is correct.';
  463.       With inp_rec do
  464.       case fld_no of
  465.         1     : begin Write(inp_str); borrower := inp_str; end;
  466.         2     : begin Write(inp_str); collateral := inp_str; end;
  467.         3     : begin
  468.                   Val(inp_str,real_val,io_status); input_ok := (io_status = 0);
  469.                   if input_ok then
  470.                     begin Write('$',real_val:11:2); principle := real_val; end;
  471.                 end;
  472.         4     : begin
  473.                   Val(inp_str,real_val,io_status); input_ok := (io_status = 0);
  474.                   if real_val <= 0.0 then real_val := 0.001;
  475.                   if input_ok then
  476.                     begin Write(real_val:6:3); rate := real_val; end;
  477.                 end;
  478.         5     : begin
  479.                   Val(inp_str,real_val,io_status); input_ok := (io_status = 0);
  480.                   if input_ok then
  481.                     begin
  482.                       Write('$',real_val:11:2); payment := real_val;
  483.                       if payment > 0.0 then no_of_pmts := 0;
  484.                     end;
  485.                 end;
  486.         6     : begin
  487.                   Val(inp_str,int_val,io_status);
  488.                   input_ok := (io_status = 0) and (int_val in [1..4,6,12,24,26,52]);
  489.                   if input_ok then
  490.                     begin
  491.                       Write(int_val:2); pmts_per_yr := int_val;
  492.                       calc_rate := (rate / pmts_per_yr / 100.0);
  493.                       if (payment > 0.0) and ((calc_rate * principle) >= payment) then
  494.                         begin
  495.                           input_ok := False;
  496.                           err_msg := 'Payment amount insuficient to pay interest.';
  497.                           fld_no := 5;
  498.                         end;
  499.                       if principle * calc_rate > 32760.0 then
  500.                         begin
  501.                           input_ok := False;
  502.                           err_msg := 'Values exceed program limits.';
  503.                           fld_no := 3;
  504.                         end;
  505.                     end
  506.                   else err_msg := 'Valid entries are 1 2 3 4 6 12 24 26 52 ';
  507.                 end;
  508.         7     : begin
  509.                   Val(inp_str,int_val,io_status);
  510.                   input_ok := (io_status = 0) and (int_val in [1..12]);
  511.                   if input_ok then
  512.                     begin Write(int_val:2); first_mo := int_val; end;
  513.                 end;
  514.         8     : begin
  515.                   Val(inp_str,int_val,io_status); input_ok := (io_status = 0);
  516.                   if input_ok then
  517.                     begin Write(int_val:2); first_yr := int_val; end;
  518.                 end;
  519.         9     : begin
  520.                   Val(inp_str,int_val,io_status); input_ok := (io_status = 0);
  521.                   if ((int_val = 0) and (payment = 0.00)) then
  522.                     input_ok := False;
  523.                   if input_ok then
  524.                     begin
  525.                       no_of_pmts := int_val;
  526.                       if int_val = 0 then Calc_No_Pmts else pmt_cnt := int_val;
  527.                       Write(pmt_cnt:5:2);
  528.                       Calc_Last_Yr;
  529.                     end
  530.                   else err_msg := 'Number of pmts. required if payment = 0.00. ' + retry;
  531.                 end;
  532.         10    : begin
  533.                   Val(inp_str,int_val,io_status); input_ok := False;
  534.                   if (io_status <> 0) then int_val := -99;
  535.                   if int_val = -1 then input_ok := True;
  536.                   if ((int_val >= first_yr) and (int_val <= last_yr)) then
  537.                     input_ok := True;
  538.                   if input_ok then
  539.                     begin Write(int_val:2); select_yr := int_val; end
  540.                   else
  541.                     begin
  542.                       err_msg := 'No payments due in year selected.';
  543.                       input_ok := False;
  544.                     end;
  545.                 end;
  546.         11    : begin
  547.                   input_ok := (io_status = 0) and
  548.                               (UpCase(inp_str[1]) in ['P','V']);
  549.                   if input_ok then
  550.                     begin out_to := UpCase(inp_str); Write(out_to); end
  551.                   else err_msg := 'Valid entries are P and V. ' + retry;
  552.                 end;
  553.       end;
  554.     end { Edit_Input };
  555.  
  556. procedure Disp_Error(prompt_no: Byte);
  557.     begin
  558.       Beep; Disp_Prompt(2); GoToXY(6,24); BW_Vid; Write(err_msg);
  559.       Read(kbd,inchr); Clr_Kbd_Buf; Disp_Prompt(prompt_no); Disp_Msg;
  560.     end { Disp_Error };
  561.  
  562. procedure Input_Data;
  563.     var input_ok    : Boolean;
  564.     begin
  565.       fld_no := 1;
  566.       Repeat
  567.         if msg_on then Disp_Msg;
  568.         GoToXY(6,23); BW_Vid;
  569.         Rep_Str(BL,72); GoToXY(6,23);
  570.         Disp_Default;
  571.         With fld_dat[fld_no] do
  572.           Define_Fld(fld_no,xloc,yloc,fld_len,inp_attr,disp_attr,fld_char,fld_type);
  573.         Get_Field(fld_no); if incnt = 0 then inp_str := default;
  574.         if fld_no <= last_fld then
  575.           begin
  576.             Edit_Input(input_ok);
  577.             if input_ok then fld_no := fld_no + direction
  578.             else Disp_Error(0);
  579.           end;
  580.       Until (fld_no > last_fld);
  581.     end { Input_Data };
  582.  
  583. procedure Accept_Data;
  584.     var valid_key    : Boolean;
  585.     begin
  586.       data_ok := False; Disp_Prompt(1); GoToXY(25,23);
  587.       Repeat
  588.         valid_key := True; Clr_Kbd_Buf;
  589.         Read(Kbd,inchr);
  590.         if ((inchr = ESC) and KeyPressed) then Read(Kbd,inchr);
  591.         case inchr of
  592.           CR     : data_ok  := True;
  593.           CUR_UP : Delay(1);
  594.           ESC    : dos_exit := True;
  595.           F10    : begin
  596.                      Disp_Help; Disp_Prompt(1);
  597.                      valid_key := False;
  598.                    end;
  599.           else     valid_key := False;
  600.         end;
  601.       Until valid_key;
  602.     end { Accept_Data };
  603.  
  604. procedure Load_Inp_Rec;
  605.     begin
  606.       Assign(inp_file,'LOAN.DAT');
  607.       {$I-} Reset(inp_file); {$I+} io_status := IOresult;
  608.       if io_status = OK then
  609.         begin {$I-} Read(inp_file,inp_rec); {$I+} io_status := IOresult; end;
  610.       Close(inp_file);
  611.     end { Load_Inp_Rec };
  612.  
  613. procedure Update_Inp_Rec;
  614.     begin
  615.       Assign(inp_file,'LOAN.DAT');
  616.       {$I-} Reset(inp_file); {$I+} io_status := IOresult;
  617.       if io_status = OK then
  618.         begin {$I-} Write(inp_file,inp_rec); {$I+} io_status := IOresult; end;
  619.       Close(inp_file);
  620.     end { Update_Inp_Rec };
  621.  
  622. procedure Disp_Data;
  623.     var input_ok   : Boolean;
  624.     begin
  625.       for fld_no := 1 to last_fld do
  626.          begin Re_Disp_Attr(fld_no); Disp_Default; end;
  627.     end { Disp_Data };
  628.  
  629. procedure Print_Table;
  630.     var ok_to_print, end_prt : Boolean;
  631.         pmt, line_cnt,
  632.         max_line             : Byte;
  633.         calc_pmt,
  634.         interest,
  635.         loan_balance,
  636.         total_interest,
  637.         total_payments,
  638.         princ_pmt, int_pmt,
  639.         mo_offset            : Real;
  640.         yr_total             : Array[1..3] of Real;
  641.         output_device        : String[4];
  642.         out_file             : Text;
  643.  
  644.     function Mos_Per_Pmt: Real;
  645.         begin
  646.           Mos_Per_Pmt := 12 / inp_rec.pmts_per_yr;
  647.         end { Mos_Per_Pmt };
  648.  
  649.     procedure Calc_Payment;
  650.         var  cents, temp  : Real;
  651.  
  652.         function Adj_Rate(rate,pmts: Real): Real;
  653.             var i           : Byte;
  654.                 accum_rate, one_plus_rate  : Real;
  655.             begin
  656.               accum_rate := 1.0; one_plus_rate := 1.0 + rate;
  657.               for i := 1 to trunc(pmts) do
  658.                  accum_rate := (accum_rate / one_plus_rate);
  659.               Adj_Rate := accum_rate;
  660.             end { Adj_Rate };
  661.  
  662.         begin { Calc_Payment }
  663.           calc_pmt := inp_rec.principle * calc_rate
  664.                       / (1 - Adj_Rate(calc_rate,pmt_cnt));
  665.           With fld_dat[5] do GoToXY(xloc,yloc); temp := calc_pmt;
  666.           Re_Disp_Attr(5); Write(calc_pmt:11:2); GoToXY(40,25);
  667.           cents := Frac(calc_pmt);
  668.           calc_pmt := Trunc(temp) + (Round(cents * 100.0) * 0.01);
  669.         end { Calc_Payment };
  670.  
  671.     procedure Print_Period(pmt_no: Integer);
  672.         type  Month_Str         = String[3];
  673.         var   prt_mo            : array[1..12] of Month_Str;
  674.               mo_str            : String[48] absolute prt_mo;
  675.               mo_out            : String[3];
  676.               int_due, prin_pd  : Real;
  677.               j, yr_out         : Integer;
  678.  
  679.         procedure Calc_Period;
  680.             var cents, temp  : Real;
  681.             begin
  682.               int_due := (loan_balance * calc_rate); temp := int_due;
  683.               cents := Frac(int_due);
  684.               int_due := Trunc(temp) + (Round(cents * 100.0) * 0.01);
  685.               if (loan_balance + int_due) < calc_pmt
  686.                 then calc_pmt := (loan_balance + int_due);
  687.               prin_pd := calc_pmt - int_due;
  688.               total_interest := total_interest + int_due;
  689.               total_payments := total_payments + calc_pmt;
  690.               loan_balance := loan_balance - prin_pd;
  691.               if ((inp_rec.select_yr = -1) or (inp_rec.select_yr = yr_out)) then
  692.                 begin
  693.                   yr_total[1] := yr_total[1] + calc_pmt;
  694.                   yr_total[2] := yr_total[2] + prin_pd;
  695.                   yr_total[3] := yr_total[3] + int_due;
  696.                 end;
  697.             end { Calc_Period };
  698.  
  699.         begin
  700.           mo_str := 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec';
  701.           for j := 0 to 11 do
  702.              mo_str[j * 4] := chr(3);
  703.           mo_out := prt_mo[(inp_rec.first_mo + Round(mo_offset) - 1) mod 12 + 1];
  704.           yr_out := inp_rec.first_yr +
  705.                     ((Round(mo_offset) + inp_rec.first_mo - 1) div 12);
  706.           Calc_Period;
  707.           if (inp_rec.select_yr = -1) or (inp_rec.select_yr = yr_out) then
  708.             begin
  709.               if yr_out < 80 then yr_out := yr_out + 2000
  710.               else yr_out := yr_out + 1900;
  711.               WriteLn(out_file,(pmt_no + 1):3,mo_out:5,yr_out:5,
  712.                     loan_balance:11:2,calc_pmt:12:2,prin_pd:12:2,int_due:12:2);
  713.               line_cnt := line_cnt + 1;
  714.             end;
  715.         end { Print_Period };
  716.  
  717.     procedure Print_Header;
  718.         begin
  719.           WriteLn(out_file,' Payment       Remaining     Total       Principle   Interest');
  720.           WriteLn(out_file,' No./Date      Principle     Payment     Payment     Payment');
  721.           WriteLn(out_file,' ------------------------------------------------------------');
  722.           line_cnt := 3;
  723.         end { Print_Header };
  724.  
  725.     procedure Print_Yr_Totals;
  726.         var  j  : Byte;
  727.         begin
  728.           if inp_rec.out_to = 'P' then WriteLn(out_file);
  729.           Write(out_file,'Yearly Totals',loan_balance:11:2);
  730.           for j := 1 to 3 do
  731.              begin
  732.                Write(out_file,yr_total[j]:12:2);
  733.                yr_total[j] := 0.0;
  734.              end;
  735.           WriteLn(out_file); line_cnt := line_cnt + 1;
  736.           if inp_rec.out_to = 'P' then
  737.             begin WriteLn(out_file); line_cnt := line_cnt + 2; end;
  738.         end { Print_Yr_Totals };
  739.  
  740.     procedure New_Page(out_dev: Char);
  741.         begin
  742.           if out_dev = 'P' then
  743.             Write(out_file,TOF)
  744.           else
  745.             begin
  746.               ClrScr; Print_Prompt; GoToXY(1,1);
  747.             end;
  748.         end { New_Page };
  749.  
  750.     procedure Ok_To_Cont;
  751.         begin
  752.           GoToXY(1,23); Write('MSG: ',retry,ARROW);
  753.           Repeat Delay(1) Until KeyPressed;
  754.           Read(Kbd,inchr); Clr_Kbd_Buf;
  755.           if inchr=ESC then end_prt := True;
  756.           Move(Prompt_line[5],vid_line[23],180);
  757.         end { Ok_To_Cont };
  758.  
  759.     procedure Print_Desc;
  760.         var year  : Integer;
  761.         begin
  762.           With inp_rec do
  763.           begin
  764.             WriteLn(out_file,'AMORTIZATION SCHEDULE':52); WriteLn(out_file);
  765.             WriteLn(out_file,' Borrower    : ',borrower);
  766.             WriteLn(out_file,' Collateral  : ',collateral); WriteLn(out_file);
  767.             WriteLn(out_file,' Principle   : ',principle:11:2,'  Interest Rate : ',rate:5:3);
  768.             WriteLn(out_file,' Pmts per Yr : ',pmts_per_yr:2,' ':11,'Number Of Pmts: ',pmt_cnt:5:2);
  769.             WriteLn(out_file);
  770.             if select_yr = -1 then
  771.               WriteLn(out_file,' Complete Schedule')
  772.            else
  773.               begin
  774.                 if select_yr < 80 then year := select_yr + 2000
  775.                 else year := select_yr + 1900;
  776.                 WriteLn(out_file,' Schedule for ',year);
  777.               end;
  778.             WriteLn(out_file);
  779.           end;
  780.         end { Print_Desc };
  781.  
  782.     procedure Print_Summary;
  783.         begin
  784.           WriteLn(out_file,CR,'Loan Totals ',' ':12,total_payments:12:2,
  785.                   ' ':12,total_interest:12:2);
  786.         end;
  787.  
  788.     begin { Print Table }
  789.       Print_Prompt; GoToXY(6,24); BW_Vid;
  790.       Write('Press ',ENTER,' When ready to print ',ARROW);
  791.       Repeat Read(Kbd,inchr);
  792.       Until (inchr = CR) or ((inchr = ESC) and (not KeyPressed));
  793.       if inchr = CR then
  794.         begin
  795.           Move(prompt_line[21],vid_line[25],160);
  796.           total_interest := 0.0; total_payments := 0.0;
  797.           for j := 1 to 3 do
  798.              yr_total[j] := 0.0;
  799.           With inp_rec do
  800.           begin
  801.             loan_balance := principle; mo_offset := 0.0;
  802.             total_interest := 0.0; total_payments := 0.0;
  803.             calc_rate := (rate / pmts_per_yr / 100.0);
  804.             if no_of_pmts = 0 then Calc_No_Pmts else pmt_cnt := no_of_pmts;
  805.             if payment = 0.0 then Calc_Payment else calc_pmt := payment;
  806.             if out_to = 'P' then
  807.               begin max_line := 56; output_device := 'LST:' end
  808.             else
  809.               begin
  810.                 max_line := 20; output_device := 'CON:';
  811.                 With fld_dat[1] do
  812.                 begin
  813.                   TextColor(Fgnd(disp_attr)); TextBackground(Bgnd(disp_attr));
  814.                 end;
  815.                 New_Page(out_to);
  816.               end;
  817.             Assign(out_file,output_device); Reset(out_file);
  818.             if (out_to = 'P') then Print_Desc;
  819.             Print_Header; if (out_to = 'P') then line_cnt := line_cnt + 10;
  820.             pmt := 0; end_prt := False;
  821.             Repeat
  822.               Print_Period(pmt);
  823.               mo_offset := mo_offset + Mos_Per_Pmt;
  824.               if (pmts_per_yr in [2..12]) then
  825.                 if ((Round(mo_offset) mod 12) = 0) and (select_yr = -1) then
  826.                   Print_Yr_Totals;
  827.               if line_cnt > max_line then
  828.                 begin
  829.                   if out_to = 'P' then New_Page(out_to)
  830.                   else begin Ok_To_Cont; New_Page(out_to); end;
  831.                   Print_Header;
  832.                 end;
  833.               pmt := pmt + 1;
  834.               if KeyPressed then Read(Kbd,inchr);
  835.               if inchr = ESC then begin end_prt := True; Beep; end;
  836.               if pmt = trunc(pmt_cnt + 0.99) then end_prt := True;
  837.             Until end_prt;
  838.             if select_yr > -1 then Print_Yr_Totals;
  839.             if ((out_to = 'V') and (inchr <> ESC) and (line_cnt > 3)) then
  840.               Ok_To_Cont;
  841.             if (out_to = 'V') and (select_yr = -1) then
  842.               begin New_Page(out_to); Print_Header; end;
  843.             if (inchr <> ESC) and (select_yr = -1) then Print_Summary;
  844.             if (out_to = 'P') then New_Page(out_to) else Ok_To_Cont;
  845.           end;
  846.         end;
  847.     end { Print_Table };
  848.  
  849. begin { Loan Amortization }
  850.   ClrScr; Load_Screen; Load_Prompts; Disp_Prompt(1); msg_on := True;
  851.   last_fld := fld_dat[72].fld_len; Load_Help; esc_exit := True;
  852.   dos_exit := False; retry := ' Press ' + ENTER + ' to continue. ';
  853.   Load_Inp_Rec; data_ok := False;
  854.   if io_status = OK then
  855.     While (not dos_exit) do
  856.       begin
  857.         if esc_exit then begin Disp_Data; Clr_Kbd_Buf; Accept_Data; end;
  858.         if (not data_ok) and (not dos_exit) then
  859.           begin
  860.             esc_exit := False; Disp_Prompt(0); Input_Data;
  861.             if esc_exit then Disp_Data;
  862.             Accept_Data;
  863.           end;
  864.           if data_ok then
  865.             begin
  866.               Update_Inp_Rec; Print_Table; Load_Screen;
  867.               esc_exit := True; data_ok := False;
  868.             end;
  869.       end;
  870.     ClrScr; GoToXY(1,23); WriteLn('Session Ended');
  871. end { Loan_Amortization }.
  872.